package com.cx.common.utils;

import org.apache.commons.lang.StringUtils;

import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedTransferQueue;

/**
 * 字符串工具
 * 
 * @Author:cxy
 * @Date:2019/10/24 15:11
 */
public class StringTool {

	/** 冒号分割号 */
	public static final String COLON_SEPARATOR = ":";
	/** 空格 */
	public static final String BLANK_SEPARATOR = " ";

	private static int POOL_SIZE = (Runtime.getRuntime().availableProcessors() * 2 + 1);
	private static BlockingQueue<StringBuilder> stringBuilderList = new LinkedTransferQueue<StringBuilder>();

	private static int INITIAL_CAPACITY = 16;

	static {
		for (byte i = 0; i < POOL_SIZE; i++) {
			stringBuilderList.add(new StringBuilder(INITIAL_CAPACITY));
		}
	}

	/**
	 * 构造字符串
	 * 
	 * @param objs
	 * @return
	 */
	public static String buildString(Object... objs) {
		return buildStringWithSeparator(Splitable.ATTRIBUTE_SPLIT, objs);
	}

	/**
	 * 构造字符串
	 * 
	 * @param separator
	 * @param objs
	 * @return
	 */
	public static String buildStringWithSeparator(String separator, Object... objs) {
		String str = StringUtils.EMPTY;
		if (objs != null) {
			StringBuilder stringBuilder = getStringBuilder();
			for (int i = 0, last = objs.length - 1; i < objs.length; i++) {
				stringBuilder.append(objs[i]);
				if (i < last && separator.length() > 0) {
					stringBuilder.append(separator);
				}
			}
			str = stringBuilder.toString();
			returnStringBuilder(stringBuilder);
		}
		return str;
	}

	/**
	 * 取得Stringbuilder
	 * 
	 * @return
	 */
	public static StringBuilder getStringBuilder() {
		StringBuilder stringBuilder = stringBuilderList.poll();
		if (stringBuilder == null) {
			stringBuilder = new StringBuilder(INITIAL_CAPACITY);
		}
		return stringBuilder;
	}

	/**
	 * 返回StringBuilder
	 * 
	 * @param stringBuilder
	 */
	public static void returnStringBuilder(StringBuilder stringBuilder) {
		stringBuilder.setLength(0);
		if (stringBuilderList.size() >= POOL_SIZE) {
			return;
		}
		stringBuilderList.add(stringBuilder);
	}

	/**
	 * 
	 * @param ex
	 * @param breakFlag
	 */
	public static String buildStackTrace(Exception ex, String breakFlag) {
		// Guard against malicious overrides of Throwable.equals by
		// using a Set with identity equality semantics.
		Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
		dejaVu.add(ex);

		StringBuilder s = new StringBuilder();
		// Print our stack trace
		// s.println(this);
		s.append(ex);

		StackTraceElement[] trace = ex.getStackTrace();
		for (StackTraceElement traceElement : trace) {
			s.append(breakFlag).append("\tat ").append(traceElement);
		}

		// Print suppressed exceptions, if any
		for (Throwable se : ex.getSuppressed()) {
			printEnclosedStackTrace(se, s, trace, SUPPRESSED_CAPTION, "\t", dejaVu, breakFlag);
		}

		// Print cause, if any
		Throwable ourCause = ex.getCause();
		if (ourCause != null) {
			printEnclosedStackTrace(ourCause, s, trace, CAUSE_CAPTION, "", dejaVu, breakFlag);
		}
		return s.toString();
	}

	private static final String CAUSE_CAPTION = "Caused by: ";
	private static final String SUPPRESSED_CAPTION = "Suppressed: ";

	private static void printEnclosedStackTrace(Throwable st, StringBuilder s, StackTraceElement[] enclosingTrace,
			String caption, String prefix, Set<Throwable> dejaVu, String breakFlag) {
		if (dejaVu.contains(st)) {
			s.append("\t[CIRCULAR REFERENCE:").append(st).append("]");
		} else {
			dejaVu.add(st);
			// Compute number of frames in common between this and enclosing trace
			StackTraceElement[] trace = st.getStackTrace();
			int m = trace.length - 1;
			int n = enclosingTrace.length - 1;
			while (m >= 0 && n >= 0 && trace[m].equals(enclosingTrace[n])) {
				m--;
				n--;
			}
			int framesInCommon = trace.length - 1 - m;

			// Print our stack trace
			s.append(breakFlag).append(prefix).append(caption).append(st);
			for (int i = 0; i <= m; i++) {
				s.append(breakFlag).append(prefix).append("\tat ").append(trace[i]);
			}
			if (framesInCommon != 0) {
				s.append(breakFlag).append(prefix).append("\t... ").append(framesInCommon).append(" more");
			}
			// Print suppressed exceptions, if any
			for (Throwable se : st.getSuppressed()) {
				printEnclosedStackTrace(se, s, trace, SUPPRESSED_CAPTION, prefix + "\t", dejaVu, breakFlag);
			}

			// Print cause, if any
			Throwable ourCause = st.getCause();
			if (ourCause != null) {
				printEnclosedStackTrace(ourCause, s, trace, CAUSE_CAPTION, prefix, dejaVu, breakFlag);
			}
		}
	}
}
